package com.ybear.ybutils.utils

import android.annotation.SuppressLint
import android.content.Context
import android.content.res.AssetManager
import android.content.res.ColorStateList
import android.content.res.Resources.NotFoundException
import android.content.res.Resources.Theme
import android.content.res.XmlResourceParser
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.text.Html
import android.text.Html.ImageGetter
import android.text.Html.TagHandler
import android.text.Spanned
import android.util.DisplayMetrics
import android.view.View
import androidx.annotation.AnimatorRes
import androidx.annotation.ArrayRes
import androidx.annotation.BoolRes
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DimenRes
import androidx.annotation.DrawableRes
import androidx.annotation.FontRes
import androidx.annotation.IntegerRes
import androidx.annotation.LayoutRes
import androidx.annotation.Px
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import androidx.annotation.XmlRes
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.ybear.ybutils.utils.log.LogUtil
import java.io.BufferedReader
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStreamReader

object ResUtil {

    /**
     * Drawable 资源图片转 Bitmap
     * @param context               上下文
     * @param vectorDrawableId      Drawable 资源图片
     * @param width                 宽度。-1: 图片默认宽度
     * @param height                高度。-1: 图片默认高度
     */
    @JvmOverloads
    @JvmStatic
    fun drawableToBitmap(context: Context,
                         @DrawableRes vectorDrawableId: Int,
                         width: Int, height: Int,
                         config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap? {
        val res = context.resources
        val drawable = VectorDrawableCompat.create(
            res, vectorDrawableId, null
        ) ?: return BitmapFactory.decodeResource( res, vectorDrawableId )
        val bitmap = Bitmap.createBitmap(
            width.coerceAtLeast( drawable.intrinsicWidth ),
            height.coerceAtLeast( drawable.intrinsicHeight ),
            config
        )
        val canvas = Canvas( bitmap )
        drawable.setBounds( 0, 0, canvas.width, canvas.height )
        drawable.draw( canvas )
        return bitmap
    }

    /**
     * Drawable 资源图片转 Bitmap
     * @param context               上下文
     * @param vectorDrawableId      Drawable 资源图片
     */
    @JvmOverloads
    @JvmStatic
    fun drawableToBitmap(context: Context, @DrawableRes vectorDrawableId: Int,
                         config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap? {
        return drawableToBitmap( context, vectorDrawableId, -1, -1, config )
    }

    /**
     * 控件转图片
     * @param view     控件
     * @param config   Bitmap 颜色配置
     * @param width    自定义高度
     * @param height   自定义宽度
     * @return         Bitmap
     */
    @JvmOverloads
    @JvmStatic
    fun drawToBitmap(view: View, width: Int, height: Int,
                     config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap? {
        if ( width <= 0 || height <= 0 ) return null
        val bmp = Bitmap.createBitmap( width, height, config )
        val canvas = Canvas( bmp )
        canvas.translate( -view.scaleX, -view.scaleY )
        view.draw( canvas )
        return bmp
    }

    /**
     * 控件转图片
     * @param view     控件
     * @param config   Bitmap 颜色配置
     * @return         Bitmap
     */
    @JvmOverloads
    @JvmStatic
    fun drawToBitmap(view: View, config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap? {
        if ( !view.isLaidOut || view.width <= 0 || view.height <= 0  ) {
            view.measure( 0, 0 )
            view.layout( 0, 0, view.measuredWidth, view.measuredHeight )
            return drawToBitmap( view, view.measuredWidth, view.measuredHeight, config )
        }
        return drawToBitmap( view, view.width, view.height, config )
    }

    /**
     * 纯颜色值转Bitmap
     * @param color     色值
     * @param config   Bitmap 颜色配置
     * @return         Bitmap
     */
    @JvmOverloads
    @JvmStatic
    fun drawToBitmap(@ColorInt color: Int, width: Int, height: Int,
                     config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap {
        val bitmap = Bitmap.createBitmap( width, height, config )
        val canvas = Canvas(bitmap)
        canvas.drawColor(color)
        return bitmap
    }

    /**
     * 控件转图片
     * @param resId    控件布局id
     * @param config   Bitmap 颜色配置
     * @param width    自定义高度
     * @param height   自定义宽度
     * @return         Bitmap
     */
    @JvmOverloads
    @JvmStatic
    fun drawToBitmap(context: Context, @LayoutRes resId: Int, width: Int, height: Int,
                     config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap? {
        return drawToBitmap( View.inflate( context, resId, null ), width, height, config )
    }

    /**
     * 控件转图片
     * @param resId    控件布局id
     * @param config   Bitmap 颜色配置
     * @return         Bitmap
     */
    @JvmOverloads
    @JvmStatic
    fun drawToBitmap(context: Context, @LayoutRes resId: Int,
                     config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap? {
        return drawToBitmap( View.inflate( context, resId, null ), config )
    }

    /**
     * Uri 转 Bitmap
     */
    @JvmStatic
    fun uriToBitmap(context: Context, uri: Uri): Bitmap? {
        return try {
            val inputStream = context.contentResolver.openInputStream( uri )
            val bitmap = BitmapFactory.decodeStream( inputStream )
            inputStream?.close()
            bitmap
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    /**
     * View 转 Uri
     */
    @JvmOverloads
    @JvmStatic
    fun viewToUri(context: Context,
                  view: View,
                  config: Bitmap.Config = Bitmap.Config.ARGB_8888,
                  quality: Int = 100,
                  fileName: String = "tmp_view_to_uri.png") : Uri {
        // 创建内存中的缓存
        val bytes = ByteArrayOutputStream()
        // 将 Bitmap 压缩到缓存中
        drawToBitmap( view, config )?.compress( Bitmap.CompressFormat.PNG, quality, bytes )
        val path = fileToUri( context, File( context.cacheDir, fileName ) )
        try {
            // 将缓存内容写入文件
            context.contentResolver.openOutputStream( path )?.use { it.write( bytes.toByteArray() ) }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return path
    }

    /**
     * Bitmap 转 Uri
     */
    @JvmOverloads
    @JvmStatic
    fun bitmapToUri(context: Context,
                    bitmap: Bitmap,
                    quality: Int = 100,
                    fileName: String = "tmp_bitmap_to_uri.png"): Uri? {
        // 获取应用缓存目录
        val cacheDir = File( context.externalCacheDir, "images" )
        cacheDir.mkdirs() // 如果目录不存在，则创建
        val file = File( cacheDir, fileName )
        try {
            FileOutputStream( file ).use { out ->
                bitmap.compress( Bitmap.CompressFormat.PNG, quality, out )
            }
            return fileToUri( context, file )
        } catch (e: IOException) {
            e.printStackTrace()
            return null
        }
    }

    /**
     * Uri 转 File
     */
    @JvmStatic
    fun uriToFile(uri: Uri): File {
        if ( uri.scheme == "file" ) {
            val filePath = uri.path
            if ( filePath != null ) return File( filePath )
        }
        return File( uri.path ?: "" )
    }

    /**
     * Uri 转 图片File
     */
    @JvmStatic
    fun uriToImageFile(context: Context, uri: Uri): File? {
        var path: String? = null
        if ( isFile( uri ) ) {
            path = uri.encodedPath
            if (path != null) {
                path = Uri.decode(path)
                val cr = context.contentResolver
                val cur = cr.query(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, arrayOf(
                        MediaStore.Images.ImageColumns._ID,
                        MediaStore.Images.ImageColumns.DATA
                    ), String.format("(%s='%s')", MediaStore.Images.ImageColumns.DATA, path),
                    null,
                    null
                )
                var index = 0
                var dataIdx: Int
                assert(cur != null)
                cur!!.moveToFirst()
                while (!cur.isAfterLast) {
                    index = cur.getColumnIndex(MediaStore.Images.ImageColumns._ID)
                    index = cur.getInt(index)
                    dataIdx = cur.getColumnIndex(MediaStore.Images.ImageColumns.DATA)
                    path = cur.getString(dataIdx)
                    cur.moveToNext()
                }
                cur.close()
                if (index != 0) {
                    val u = Uri.parse("content://media/external/images/media/$index")
                    LogUtil.d("uriToFile -> $u")
                }
            }
            if (path != null) {
                return File(path)
            }
        } else if ( isContent( uri ) ) {
            // 4.2.2以后
            val projection = arrayOf(MediaStore.Images.Media.DATA)
            val cursor = context.contentResolver.query(
                uri, projection, null, null, null
            ) ?: return null
            if ( cursor.moveToFirst() ) {
                val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
                path = try { cursor.getString(columnIndex) } catch (_: Exception) { null }
            }
            try { cursor.close() } catch (_: Exception) { }
            //4.4+解决办法
            if ( path.isNullOrEmpty() ) path = GetPathFromUri4kitkat.getPath( context, uri )
            return if( path == null ) null else File( path )
        }
        return null
    }

    /**
     * 将uri转为真实路径（不推荐）
     * @param ctx       上下文
     * @param uri       uri路径
     * @return          返回真实路径
     */
    @JvmStatic
    fun uriToPath(ctx: Context, uri: Uri?): String? {
        var str: String? = null
        val path = uri?.path
        val index: Int
        val projection = arrayOf(MediaStore.Images.Media.DATA)
        val cr = ctx.contentResolver
        if (uri != null) {
            cr.query( uri, projection, null, null, null )?.apply {
                //移动游标到第一个位置
                moveToFirst()
                //获取索引
                index = getColumnIndexOrThrow( projection[ 0 ] )
                //获取当前位置的数据
                if ( count > 0 ) str = getString( index )
                close()
            }
            //4.4+解决办法
            if ( str.isNullOrEmpty() ) str = GetPathFromUri4kitkat.getPath( ctx, uri )
        }
        return str ?: path
    }

    /**
     * 文件大小
     * @param file  文件
     * @return      返回文件大小（-1获取失败）
     */
    @JvmStatic
    fun getFileSize(file: File?): Int {
        try {
            val fis = FileInputStream( file )
            return fis.available()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return -1
    }

    /**
     * 路径 是否为 Uri
     */
    @JvmStatic
    fun isUri(path: String?) : Boolean {
        val uri = if( path.isNullOrBlank() ) null else Uri.parse( path )
        return uri != null && uri.scheme != null
    }

    /**
     * Uri 是否为 File
     */
    @JvmStatic
    fun isFile(uri: Uri?) : Boolean { return uri == null || uri.scheme != "file" }

    /**
     * Uri 是否为 content://
     */
    @JvmStatic
    fun isContent(uri: Uri?) : Boolean { return uri == null || uri.scheme != "content" }

    /**
     * 获取Bitmap内存大小
     */
    @JvmStatic
    fun getBitmapSize(bitmap: Bitmap): Int { return bitmap.allocationByteCount }

    /**
     *    1.需要在 res/xml 目录下创建一个名为 file_paths.xml
     *      <!-- 如果已经配置过 external-cache-path，则无需配置 -->
     *      <?xml version="1.0" encoding="utf-8"?>
     *       <paths xmlns:android="http://schemas.android.com/apk/res/android">
     *           <external-cache-path name="temp_images" path="images/"/>
     *       </paths>
     *    2.清单文件须配置 AndroidManifest.xml
     *      <!-- authorities路径必须与调用路径一致。applicationId 等价 context.packageName -->
     *      <provider
     *        android:name="androidx.core.content.FileProvider"
     *        android:authorities="${applicationId}.provider"
     *        android:exported="false"
     *        android:grantUriPermissions="true">
     *        <meta-data
     *          android:name="android.support.FILE_PROVIDER_PATHS"
     *          android:resource="@xml/file_paths" />
     *      </provider>
     */
    @JvmOverloads
    @JvmStatic
    fun fileToUri(context: Context, file: File, authorities: String = "${context.packageName}.provider") : Uri {
        return FileProvider.getUriForFile( context, authorities, file )
    }

    /**
     * 支持显示图片的fromHtml
     *
     * `"<img src='ic_test_icon'></img>"`
     * ic_test_icon为图片资源，即：R.drawable.ic_test_icon
     * @param context       上下文
     * @param content       文本
     * @param cls           `R.drawable.class`
     * @param resName       资源名称。eg：ic_test_icon
     * @return              结果
     */
    @JvmOverloads
    @JvmStatic
    fun fromHtmlOfResImg(context: Context,
                         content: String,
                         tagHandler: TagHandler? = null,
                         cls: Class<*>,
                         vararg resName: String?): Spanned {
        var tmpContent = content
        if ( resName.isNotEmpty() ) {
            val resStr = StringBuilder()
            for (s in resName) resStr.append("<img src='").append(s).append("'/>")
            tmpContent = String.format( tmpContent, resStr.toString() )
        }
        val call = object : ImageGetter {
            override fun getDrawable(source: String?): Drawable? {
                try {
                    //检查是否为传入的类
                    if ( source == null || cls.getField( source )[ null ] == null ) return null
                    val d = getDrawable( context, source ) ?: return null
                    d.setBounds(0, 0, d.intrinsicWidth, d.intrinsicHeight)
                    return d
                } catch (e: Exception) {
                    return null
                }
            }

        }
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Html.fromHtml( tmpContent, Html.FROM_HTML_MODE_LEGACY, call, tagHandler )
        } else {
            Html.fromHtml( tmpContent, call, tagHandler )
        }
    }

    @JvmStatic
    fun getIdentifier(context: Context, name: String?, type: String?): Int {
        try {
            return context.resources.getIdentifier(name, type, context.packageName)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return 0
    }

    @JvmStatic
    fun getViewId(context: Context, name: String): Int {
        return getIdentifier(context, name, "id")
    }

    @JvmStatic
    fun getLayoutId(context: Context, name: String): Int {
        return getIdentifier(context, name, "layout")
    }

    @JvmStatic
    fun getAnimId(context: Context, name: String): Int {
        return getIdentifier(context, name, "anim")
    }

    @JvmStatic
    fun getAnimatorId(context: Context, name: String): Int {
        return getIdentifier(context, name, "animator")
    }

    @JvmStatic
    fun getAttrId(context: Context, name: String): Int {
        return getIdentifier(context, name, "attr")
    }

    @JvmStatic
    fun getBoolId(context: Context, name: String): Int {
        return getIdentifier(context, name, "bool")
    }

    @JvmStatic
    fun getIntegerId(context: Context, name: String): Int {
        return getIdentifier(context, name, "integer")
    }

    @JvmStatic
    fun getDimenId(context: Context, name: String): Int {
        return getIdentifier(context, name, "dimen")
    }

    @JvmStatic
    fun getMipmapId(context: Context, name: String): Int {
        return getIdentifier(context, name, "mipmap")
    }

    @JvmStatic
    fun getStyleId(context: Context, name: String): Int {
        return getIdentifier(context, name, "style")
    }

    @JvmStatic
    fun getStyleableId(context: Context, name: String): Int {
        return getIdentifier(context, name, "styleable")
    }

    @JvmStatic
    fun getInterpolatorId(context: Context, name: String): Int {
        return getIdentifier(context, name, "interpolator")
    }

    @JvmStatic
    fun getXmlId(context: Context, name: String): Int {
        return getIdentifier(context, name, "xml")
    }

    @JvmStatic
    fun getStringId(context: Context, name: String): Int {
        return getIdentifier(context, name, "string")
    }

    @JvmStatic
    fun getString(context: Context, @StringRes resId: Int): String? {
        try {
            return context.resources.getString(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getString(
        context: Context,
        @StringRes resId: Int, vararg formatArgs: Any?
    ): String? {
        try {
            return context.resources.getString(resId, *formatArgs)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getString(context: Context, name: String): String? {
        return getString(context, getStringId(context, name))
    }

    @JvmStatic
    fun getTextId(context: Context, name: String): Int {
        return getIdentifier(context, name, "text")
    }

    @JvmStatic
    fun getText(context: Context, @StringRes resId: Int): CharSequence? {
        try {
            return context.resources.getText(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getText(context: Context, @StringRes resId: Int, def: CharSequence?): CharSequence? {
        try {
            return context.resources.getText(resId, def)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getText(context: Context, name: String): CharSequence? {
        return getText(context, getTextId(context, name))
    }

    @JvmStatic
    fun getTextArrayId(context: Context, name: String): Int {
        return getIdentifier(context, name, "textArray")
    }

    @JvmStatic
    fun getTextArray(
        context: Context, @ArrayRes resId: Int,
        def: Array<CharSequence>
    ): Array<CharSequence>? {
        try {
            return getTextArray(context, resId)
        } catch (e: NotFoundException) {
            e.printStackTrace()
        }
        return def
    }

    @JvmStatic
    fun getTextArray(context: Context, @ArrayRes resId: Int): Array<CharSequence>? {
        try {
            return context.resources.getTextArray(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getTextArray(context: Context, name: String): Array<CharSequence>? {
        return getTextArray(context, getTextArrayId(context, name))
    }

    @JvmStatic
    fun getDrawableId(context: Context, name: String): Int {
        return getIdentifier(context, name, "drawable")
    }

    @JvmStatic
    fun getDrawable(context: Context, name: String): Drawable? {
        return getDrawable(context, getDrawableId(context, name))
    }

    @SuppressLint("UseCompatLoadingForDrawables")
    @JvmStatic
    fun getDrawable(
        context: Context, @DrawableRes resId: Int,
        t: Theme?
    ): Drawable? {
        try {
            return if (t != null) {
                context.resources.getDrawable(resId, t)
            } else {
                ContextCompat.getDrawable(context, resId)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            try {
                return context.getDrawable(resId)
            } catch (e1: Exception) {
                e1.printStackTrace()
            }
        }
        return null
    }

    @JvmStatic
    @SuppressLint("UseCompatLoadingForDrawables")
    fun getDrawable(context: Context, @DrawableRes resId: Int): Drawable? {
        return getDrawable(context, resId, null)
    }

    @JvmStatic
    fun getColorId(context: Context, name: String): Int {
        return getIdentifier(context, name, "color")
    }

    @JvmStatic
    @ColorInt
    fun getColorInt(context: Context, @ColorRes resId: Int): Int {
        return getColorInt(context, resId, null)
    }

    @ColorInt
    @JvmStatic
    fun getColorInt(
        context: Context, @ColorRes resId: Int,
        t: Theme?
    ): Int {
        try {
            val res = context.resources
            return if (t != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                res.getColor(resId, t)
            } else {
                ContextCompat.getColor(context, resId)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return 0
    }

    @JvmStatic
    fun getColorInt(context: Context, name: String): Int {
        return getColorInt(context, getColorId(context, name))
    }

    @JvmStatic
    fun getColorStateList(context: Context, @ColorRes resId: Int): ColorStateList? {
        return getColorStateList(context, resId, null)
    }

    @SuppressLint("UseCompatLoadingForColorStateLists")
    @JvmStatic
    fun getColorStateList(
        context: Context, @ColorRes resId: Int,
        t: Theme?
    ): ColorStateList? {
        try {
            val res = context.resources
            return if (t != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                res.getColorStateList(resId, t)
            } else {
                ContextCompat.getColorStateList(context, resId)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @Px
    @JvmStatic
    fun getDimension(context: Context, @DimenRes resId: Int): Long {
        try {
            return context.resources.getDimension(resId).toLong()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return 0L
    }

    @Px
    @JvmStatic
    fun getDimensionPixelOffset(context: Context, @DimenRes resId: Int): Long {
        try {
            return context.resources.getDimensionPixelOffset(resId).toLong()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return 0L
    }

    @Px
    @JvmStatic
    fun getDimensionPixelSize(context: Context, @DimenRes resId: Int): Long {
        try {
            return context.resources.getDimensionPixelSize(resId).toLong()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return 0L
    }

    @JvmStatic
    fun getBool(context: Context, @BoolRes resId: Int): Boolean {
        try {
            return context.resources.getBoolean(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return false
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    @JvmStatic
    fun getFloat(context: Context, @DimenRes resId: Int): Float {
        try {
            return context.resources.getFloat(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return 0f
    }

    @JvmStatic
    fun getInt(context: Context, @IntegerRes resId: Int): Int {
        try {
            return context.resources.getInteger(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return 0
    }

    @JvmStatic
    fun getIntArray(context: Context, @ArrayRes resId: Int): IntArray? {
        try {
            return context.resources.getIntArray(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    @JvmStatic
    fun getFont(context: Context, @FontRes resId: Int): Typeface? {
        try {
            return context.resources.getFont(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getLayout(context: Context, @LayoutRes resId: Int): XmlResourceParser? {
        try {
            return context.resources.getLayout(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getAnim(context: Context, @AnimatorRes resId: Int): XmlResourceParser? {
        try {
            return context.resources.getAnimation(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getXml(context: Context, @XmlRes resId: Int): XmlResourceParser? {
        try {
            return context.resources.getXml(resId)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getAssets(context: Context): AssetManager? {
        try {
            return context.resources.assets
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getDisplayMetrics(context: Context): DisplayMetrics? {
        try {
            return context.resources.displayMetrics
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getAssetsString(context: Context, fileName: String?): String {
        val sb = StringBuilder()
        val isr: InputStreamReader
        val br: BufferedReader
        var len: Int
        try {
            isr = InputStreamReader(context.assets.open(fileName!!))
            br = BufferedReader(isr)
            while (br.read().also { len = it } != -1) {
                sb.append(len.toChar())
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return sb.toString()
    }
}
